package wiiusej.apptest.activityTasks;

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Vector;
import java.util.concurrent.Callable;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.Priority;

import wiiusej.Wiimote;
import wiiusej.activeTaskManagement.SoundShareCell;
import wiiusej.apptest.WiimoteEventListener;
import wiiusej.apptest.statistics.AccelerationPaquet;






public class RealTimeFeedback implements Callable{

	private Vector<AccelerationPaquet> myEventVector;
	
	private AccelerationPaquet need2Check;
	
	//Acceleration Tresholds	
	private int robotUp = 240;
	private int robotDown = 30;
	
	private int bigSoftUp = 188;
	private int bigSoftDown = 100;
	
	private int middle = 144;	
	
	//Logger
	static Logger fileLogger;
	
	private Wiimote myWiimote;
	private SoundShareCell soundCell;
	
	private static final String fileURL = "src/test/java/wiiusej/apptest/statistics/logs/RealTimeFeedback";
	
	//Speaker Parameters
	private static final int FIRST_LEVEL_FREQ = 2000;
	private static final int SECOND_LEVEL_FREQ = 1500;
	private static final int THIRD_LEVEL_FREQ = 1000;
	private static final int FOURTH_LEVEL_FREQ = 500;
	private static final int FIFTH_LEVEL_FREQ = 250;
	private static final int SIXTH_LEVEL_FREQ = 125;
	
	private WiimoteEventListener myListener;
	
	//RealTime APP modes
	private final int MODE_SLOWMOTION = 0;
	private final int MODE_BIGSOFTMOTION = 1;
	private final int MODE_ROBOTIC = 2;
	
	private int gameMode;
	
	public RealTimeFeedback(Vector<AccelerationPaquet> myEventVector, Long id, 
			AccelerationPaquet need2Check, SoundShareCell soundCell, Wiimote wiimote, 
			WiimoteEventListener myListener, int mode){
		
		this.gameMode = mode;
		this.myEventVector = myEventVector;
		this.need2Check = need2Check;
		
		this.myWiimote = wiimote;
		this.soundCell = soundCell;
		this.myListener = myListener;
		
		fileLogger = Logger.getLogger("realTimeFeedback");
		
		PatternLayout pattern = 
			new PatternLayout("%m%n");
		
		try{
			FileAppender fileappend=new FileAppender(pattern,fileURL + "_" + id + ".log");
			fileappend.setThreshold(Priority.INFO);			
			fileLogger.addAppender(fileappend);			
		}
		catch(Exception e){
			
		}		
		
	}	
	
	@Override
	public AccelerationPaquet call() throws Exception {

		SimpleDateFormat tsFormat = new SimpleDateFormat("mm:ss:SS");
		AccelerationPaquet lastElement = myEventVector.lastElement();
		
		
		
		if(need2Check == null){
			need2Check = myEventVector.firstElement();
			return need2Check; 
		}		
		
		int minuteRest = lastElement.getTime().getMinutes() - need2Check.getTime().getMinutes();
		int secondRest = lastElement.getTime().getSeconds() - need2Check.getTime().getSeconds();		
		
		
		fileLogger.debug("need2Check: " + tsFormat.format(need2Check.getTime())
				+ " EventListSize: " + myEventVector.size());
		
		
		
		
		//we are in the same minute
		if(minuteRest == 0){
			
			if(secondRest == 1){
				//new consecutive element has arrived, so, we have to check the one before last
				calculeLastPaquetMean(need2Check, tsFormat);
				
				//change the pointer to the last arrived paquet
				need2Check = myEventVector.lastElement();
				
				fileLogger.debug("**************************************************************************");
				fileLogger.debug("New needToCheck: " + tsFormat.format(need2Check.getTime())); 
				return need2Check;
				
				//many paquets has been added due to paquet lost --> [1 5 0 0 0 0 1]
			}else if(secondRest > 1){
				
				calculeLastPaquetMean(need2Check, tsFormat);
				int index = myEventVector.indexOf(need2Check);
							
				AccelerationPaquet newNeed2Check = findNewNextToCheck(index);
				
				if(newNeed2Check != null){
					fileLogger.debug("**************************************************************************");
					fileLogger.debug("New Need2Check Found: " + tsFormat.format(newNeed2Check.getTime()));
					return newNeed2Check;
					
				}else{
					fileLogger.debug("**************************************************************************");
					fileLogger.debug("ERROR FOUNDING NEW NEXT TO CHECK");
					return null;
				}
			}
			
			
			
			
		}
		
		if(minuteRest > 0){
			//we are not in the same minute
			
			if(need2Check.getTime().getSeconds() == 59 
					&& lastElement.getTime().getSeconds() == 0){
				//they are consecutive
				
				calculeLastPaquetMean(need2Check, tsFormat);
				
				//change the pointer to the last arrived paquet
				need2Check = myEventVector.lastElement();
				
				fileLogger.debug("**************************************************************************");
				fileLogger.debug("New needToCheck: " + tsFormat.format(need2Check.getTime())); 
				return need2Check;
			}
			else{
				
				calculeLastPaquetMean(need2Check, tsFormat);
				int index = myEventVector.indexOf(need2Check);
							
				AccelerationPaquet newNeed2Check = findNewNextToCheck(index);
				
				if(newNeed2Check != null){
					fileLogger.debug(" ");
					fileLogger.debug("New Need2Check Found: " + tsFormat.format(newNeed2Check.getTime()));
					return newNeed2Check;
					
				}else{
					fileLogger.debug("**************************************************************************");
					fileLogger.debug("ERROR FOUNDING NEW NEXT TO CHECK");
					return null;
				}
				
			}
			
		}		
		
		return need2Check;
		
		
		
	}

	private AccelerationPaquet findNewNextToCheck(int index){
		
		for(AccelerationPaquet aux : myEventVector){
			
			if((myEventVector.indexOf(aux)  > index) ){
				
				if(aux.getCounter() > 0){
					return aux;
				}				
			}else{
				//nothing
			}
			
		}		
		
		return null;
	}
	
	private void calculeLastPaquetMean(AccelerationPaquet nextToLastChecked, SimpleDateFormat tsFormat) {
		
		int meanX, meanY, meanZ;
		
		meanX = meanAxis((Vector<Integer>) nextToLastChecked.getAccX().clone());
		meanY = meanAxis((Vector<Integer>) nextToLastChecked.getAccY().clone());
		meanZ = meanAxis((Vector<Integer>) nextToLastChecked.getAccZ().clone());
		
		
		switch(gameMode){
		
			case MODE_SLOWMOTION:
				giveFeedback(meanX, meanY, meanZ);
				break;
		
			case MODE_BIGSOFTMOTION:
				giveFeedback(meanX, meanY, meanZ);
				break;
			
			case MODE_ROBOTIC:
				giveRobotFeedback(nextToLastChecked);
				break;
		}		
		
		printReport(meanX, meanY, meanZ, nextToLastChecked, tsFormat);		
		
		
	}

	private int meanAxis(Vector<Integer> axisVector){
		
		int mean = 0; 
		for(Integer i : axisVector){
			
			if(i<= 85){
				i = 255 - i;
			}
			
			mean = mean + i;
		}
		
		return mean/axisVector.size();
	}
	
	private void printReport(int meanX, int meanY, int meanZ, AccelerationPaquet nextToLastChecked,
			SimpleDateFormat tsFormat){
		
		fileLogger.info(" ");
		fileLogger.info("Paquet: " + tsFormat.format(nextToLastChecked.getTime())
				+ " MeanX: " + meanX
				+ " MeanY: " + meanY
				+ " MeanZ: " + meanZ);		
		
		fileLogger.info("VectorZ: "  + nextToLastChecked.printFullList());
	}
	
	private void giveFeedback(int meanX, int meanY, int meanZ){
		
		if( meanZ<155 || meanY < 155 || meanX < 155){
			//Low motion
			fileLogger.info("MeanZ : " + meanZ + " LOW MOTION");
			soundCell.setSoundFrequencyMillis(FIRST_LEVEL_FREQ);	
			//FastMotion Detector
			rumbleMote();
		}
		
		if( ((meanZ >= 155) && (meanZ < 170)) ||
				((meanX >= 155) && (meanX < 170)) ||
				((meanY >= 155) && (meanY < 170))){
			//Normal Motion
			fileLogger.info("MeanZ : " + meanZ + " NORMAL MOTION");
			soundCell.setSoundFrequencyMillis(THIRD_LEVEL_FREQ);			
		}
		
		
		if(meanZ >= 170 || meanX >= 170 || meanY >= 170){
			//BigSoftMotion
			fileLogger.info("MeanZ : " + meanZ + " BIG SOFT MOTION");
			soundCell.setSoundFrequencyMillis(FIFTH_LEVEL_FREQ);
			//SlowMotionDetector
						
		}		
		
	}
	
	private void giveRobotFeedback(AccelerationPaquet nextToLastChecked){
		
		soundCell.setSoundFrequencyMillis(FIRST_LEVEL_FREQ);		
		
		if(detectPeaks(nextToLastChecked)){
			rumbleMote();
			soundCell.setSoundFrequencyMillis(FIFTH_LEVEL_FREQ);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			
			
		}
		
	}
	
	private boolean detectPeaks(AccelerationPaquet nextToLastChecked){
		
		boolean peak = false;
		
		for(Integer i : nextToLastChecked.getAccX()){
			if(i > 190 || i < 95){
				peak = true;
			}			
		}
		
		for(Integer i : nextToLastChecked.getAccY()){
			if(i > 190 || i < 95){
				peak = true;
			}			
		}
		
		for(Integer i : nextToLastChecked.getAccZ()){
			if(i > 190 || i < 95){
				peak = true;
			}			
		}
		
		return peak;
	}
	
	
	
	private void rumbleMote(){		
		myWiimote.activateRumble();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	
		myWiimote.deactivateRumble();
		
	}
	
	




	
	
	
	
	
}
